package io.lvdaxian.middleware.db.router;

import io.lvdaxian.middleware.db.router.annotation.DBRouter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component("db-router-point")
public class DBRouterJoinPoint {
  
  private final Logger logger = LoggerFactory.getLogger(DBRouterJoinPoint.class);
  
  @Autowired
  private DBRouterConfig dbRouterConfig;
  
  @Pointcut("@annotation(io.lvdaxian.middleware.db.router.annotation.DBRouter)")
  public void aopPoint() {
  }
  
  @Around("aopPoint() && @annotation(dbRouter)")
  public Object doRouter(ProceedingJoinPoint jp, DBRouter dbRouter) throws Throwable {
    // 这是要 路由的 route key
    String dbKey = dbRouter.key();
    // 如果为空的话，直接报异常
    if (StringUtils.isBlank(dbKey))
      throw new RuntimeException("annotation DBRouter key is null！");
    // 计算路由
    String dbKeyAttr = getAttrValue(dbKey, jp.getArgs());
    int size = dbRouterConfig.getDbCount() * dbRouterConfig.getTbCount();
    // 扰动函数
    // dbKeyAttr.hashCode() >>> 16 这个值参与运算的话，是为了code 的低16位 以及高16位 都要参与运算。
    // 最大可能避免冲突
    int idx = (size - 1) & (dbKeyAttr.hashCode() ^ (dbKeyAttr.hashCode() >>> 16));
    // 库表索引
    int dbIdx = idx / dbRouterConfig.getTbCount() + 1;
    int tbIdx = idx - dbRouterConfig.getTbCount() * (dbIdx - 1);
    
    // 设置到 ThreadLocal
    DBContextHolder.setDBKey(String.format("%02d", dbIdx));
    DBContextHolder.setTBKey(String.format("%02d", tbIdx));
    logger.info("数据库路由 method：{} dbIdx：{} tbIdx：{}", getMethod(jp).getName(), dbIdx, tbIdx);
    // 返回结果
    try {
      return jp.proceed();
    } finally {
      DBContextHolder.clearDBKey();
      DBContextHolder.clearTBKey();
    }
  }
  
  private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
    Signature sig = jp.getSignature();
    MethodSignature methodSignature = (MethodSignature) sig;
    return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
  }
  
  /**
   * 从这里 通过key 拿到 value
   *
   * @param attr 属性值
   * @param args 这是参数
   * @return
   * @author lihh
   */
  public String getAttrValue(String attr, Object[] args) {
    String filedValue = null;
    for (Object arg : args) {
      try {
        if (StringUtils.isNotBlank(filedValue)) break;
        filedValue = BeanUtils.getProperty(arg, attr);
      } catch (Exception e) {
        logger.error("获取路由属性值失败 attr：{}", attr, e);
      }
    }
    return filedValue;
  }
  
}
